package test.client.analyzer;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import test.client.support.thread.ThreadManager;
import test.client.support.time.TimeTracker;
import test.client.thread.BaseThread;
import test.client.thread.BaseThread.Result;
import test.domain.environment.file.ResultFile;
import util.FileManager;

public class DefaultAnalyzer implements Analyzer {
private Log log = LogFactory.getLog(this.getClass());
	
	private ThreadManager threadManager;
	
	private FileManager fileManager = new FileManager();
	private ResultFile resultFile;
	
	private StringBuilder sb;
	
	@Override
	public void analyze() {
		if (threadManager == null)
			throw new IllegalStateException("Thread manager configuration was not set.");
		
		if (resultFile == null)
			throw new IllegalStateException("Result file configuration was not set.");
		
		sb = new StringBuilder();
		StringBuilder failure = new StringBuilder();
		Float sum = 0F;
		Float avg = 0F;
		
		Long maxValue = Long.MIN_VALUE;
		Long minValue = Long.MAX_VALUE;
		
		int failureThreadCount = 0;
		
		for (BaseThread thread : threadManager.getAll()) {
			if (thread.getResult() != Result.SUCCESS) {
				failureThreadCount++;
				failure.append(thread.getId() + ", " + thread.getMessage() + "\n");
			}
			else {
				Long timeAverage = this.getTimeAverage(thread.getTimeTrackers());
				if (timeAverage == -1) {
					failureThreadCount++;
					failure.append(thread.getId() + ", ERROR: Time data is invalid\n");
				}
				
				sb.append(thread.getId());
				
				for (TimeTracker tracker : thread.getTimeTrackers()) {
					sb.append(", " + tracker.getElapsed());	
				}
				
				sb.append("\n");
				sum += timeAverage / 1000F;
				
				if (timeAverage > maxValue) maxValue = timeAverage;
				if (timeAverage < minValue) minValue = timeAverage;
			}
		}
		
		// Get total average
		if (threadManager.getMaxThreadCount() > 0) {
			avg = sum / (threadManager.getMaxThreadCount() - failureThreadCount);
		}
		
		sb.append("avg, " + avg + "\n");
		sb.append("min, " + (minValue / 1000F) + "\n");
		sb.append("max, " + (maxValue / 1000F) + "\n");
		
		// check if there are threads that couldn't be started
		int startedThreadCount = threadManager.getStartedThreadCount();
		int maxThreadCount = threadManager.getMaxThreadCount(); 
		if (startedThreadCount < maxThreadCount) {
			sb.append((maxThreadCount - startedThreadCount) + " threads couldn't be started.\n");
		}
		
		failure.insert(0, "failure: " + failureThreadCount + "\n");
		sb.append(failure.toString());
		
		log.info("Analyzer: " + failureThreadCount + " threads fail. flushing...");
		try {
			String fileName = resultFile.getPath() + resultFile.getName() 
					+ "-" + this.getTimeString() + "-" + threadManager.getMaxThreadCount() + resultFile.getExt();
			fileManager.writeFile(fileName, sb);
			
			log.info("Analyzer: The table has been flushed successfully.");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private Long getTimeAverage(List<TimeTracker> trackers) {
		if (trackers == null || trackers.size() == 0) {
			return -1L;
		}
		
		Long timeSum = 0L;
		for (TimeTracker tracker : trackers) {
			if (tracker == null || tracker.getElapsed() == null) return -1L;
			timeSum += tracker.getElapsed();
		}
		
		return timeSum / trackers.size();
	}
	
	private String getTimeString() {
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");
		return sdf.format(date);
	}
	
	@Override
	public String getResult() {
		if (sb == null) return null;

		return sb.toString();
	}
	
	public DefaultAnalyzer setThreadManager(ThreadManager threadManager) {
		this.threadManager = threadManager;
		return this;
	}

	public DefaultAnalyzer setResultFile(ResultFile resultFile) {
		this.resultFile = resultFile;
		return this;
	}	
}
